package com.apcc4m.sfpush.listener;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.apcc4m.sfpush.SfpushConst;
import com.apcc4m.sfpush.bean.PushMessage;
import com.apcc4m.sfpush.bean.ServerListenerInfo;
import com.apcc4m.sfpush.handler.HttpRequestHandler;
import com.apcc4m.sfpush.handler.MyIdleHandler;
import com.apcc4m.sfpush.handler.WebSocketFrameHandler;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;

public class NettyServerListener extends ChannelInitializer<SocketChannel> {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    // 所有在线用户的列表
    private Map<String, SfChannelHandlerContext> userChannelHandlerMap;

    private ServerListenerInfo info;

    public void init(ServerListenerInfo info) {
        this.info = info;
        //多线程同时访问，ConcurrentHashMap是线程安全的
        userChannelHandlerMap = new ConcurrentHashMap<String, SfChannelHandlerContext>();

        // 用于分配处理业务线程的线程组个数
        EventLoopGroup bossGroup = new NioEventLoopGroup(info.getMainGroupSize());

        // 业务出现线程大小
        EventLoopGroup workerGroup = new NioEventLoopGroup(info.getMainThreadSize());

        // ServerBootstrap 是一个启动 NIO 服务的辅助启动类。你可以在这个服务中直接使用
        // Channel，但是这会是一个复杂的处理过程，在很多情况下你并不需要这样做。
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup, workerGroup);

        // NioServerSocketChannel继承AbstractNioMessageChannel，实现ServerSocketChannel，它是一个具体类，提供给开发者使用。
        // AbstractNioMessageChannel这个类继承AbstractNioChannel，主要是提供了一个newUnsafe方法返回NioMessageUnsafe对象的实例（实现read方法）。另外还定义doReadMessages和doWriteMessage两个抽象方法。
        // ServerSocketChannel和ServerChannel这两个接口主要是定义了一个config方法，以及获取网络地址的方法。
        serverBootstrap.channel(NioServerSocketChannel.class);

        // 添加Handler,一般用new
        // ChannelInitializer，这里使代码看上去简洁直接继承ChannelInitializer,当客户端连接上的时候会调用initChannel
        serverBootstrap.childHandler(this);
        // childOption是提供给由父管道ServerChannel接收到的连接
        serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
        // option提供给NioServerSocketChannel用来接收进来的连接
        serverBootstrap.option(ChannelOption.SO_BACKLOG, 10000);
        try {
            // 绑定端口号，启动服务，一直处于等待状态，直到服务器套接字关闭
            ChannelFuture f = serverBootstrap.bind(info.getMainPort()).sync();
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            logger.error(e.getMessage());
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    /**
     * 当有客户端连接时会触发initChannel方法
     */
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        // 用于解析http报文的handler
        pipeline.addLast(new HttpServerCodec());
        pipeline.addLast(new ChunkedWriteHandler());
        // HttpObjectAggregator会把多个消息转换为一个单一的FullHttpRequest或是FullHttpResponse
        pipeline.addLast(new HttpObjectAggregator(64 * 1024));

        // 先进行http的token验证，验证成功后对整个websocket的通信进行了初始化
        pipeline.addLast(new HttpRequestHandler(this));

        // 1:读超时秒数，2:写超时秒数，3:读写超时秒数
        // 也就是说当客户端连接上服务器后如果N秒后服务器没有再接收到相应操作则触发userEventTriggered方法中的IdleState.READER_IDLE
        // 服务器没有给客户端发送消息则触发userEventTriggered方法中的IdleState.WRITER_IDLE
        // 两个都没有则触发userEventTriggered方法中的IdleState.WRITER_IDLE
        pipeline.addLast(new IdleStateHandler(SfpushConst.HEARTBEAT_SECOND, 0, 0));
        pipeline.addLast(new MyIdleHandler(this));

        // WebSocketServerProtocolHandler它对整个websocket的通信进行了初始化，包括握手，以及以后的一些通信控制
        pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
        // 自己定义的WebSocketHandler的处理
        pipeline.addLast(new WebSocketFrameHandler(this, info.getServiceClass()));

    }

    public void saveUserChannel(String cId, ChannelHandlerContext channelHandler) {
        userChannelHandlerMap.put(cId, new SfChannelHandlerContext(channelHandler));
        System.out.println("*****saveMngChannel被调用，【" + cId + "】加入，当前用户数：【" + userChannelHandlerMap.size() + "】*****");
    }

    public void removeUserChannel(String cId) {
        userChannelHandlerMap.get(cId).close();
        userChannelHandlerMap.remove(cId);
        System.out.println(cId + "*****强制断开连接********" + userChannelHandlerMap.size());
    }

    public void push(List<String> ids, PushMessage msg) {
        for (String item : ids) {
            SfChannelHandlerContext sfctx = userChannelHandlerMap.get(item);
            if (sfctx != null) {
                sfctx.send(msg);
            }
        }
        System.out.println("*****给指定用户群发送消息********" + ids.size());
    }

    public void pushSingle(String id, PushMessage msg) {
        SfChannelHandlerContext sfctx = userChannelHandlerMap.get(id);
        if (sfctx != null) {
            sfctx.send(msg);
        }
    }

    public void pushToAll(PushMessage msg) {
        for (String key : userChannelHandlerMap.keySet()) {
            SfChannelHandlerContext sfctx = userChannelHandlerMap.get(key);
            msg.setToId(key);
            sfctx.send(msg);
        }
        System.out.println("*****给所有用户发送消息：********" + userChannelHandlerMap.size());
    }

}